package initialize

import (
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"time"

	"go.uber.org/zap"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"xincan.com.cn/tarot-go-framework/config"
	"xincan.com.cn/tarot-go-framework/global"
)

var mysqlConfig *config.MysqlConfig

// InitMysqlClient
// Title    				InitMysqlClient
// Description   			初始化mysql客户端
// Auth      				alittlexincan@163.com   		时间（2024/07/17 11:36）
// Update   				alittlexincan@163.com   		时间（2024/07/17 11:36）
// Return    				nil     						无返回值
func InitMysqlClient() *gorm.DB {
	// 1: 参数验证
	if global.AppConfig == nil || global.AppConfig.Mysql == nil {
		panic("MySQL配置: 未初始化")
	}
	mysqlConfig = global.AppConfig.Mysql.MysqlConfig()
	// 验证必要参数
	if mysqlConfig.Host == "" || mysqlConfig.Port == "" || mysqlConfig.Database == "" {
		global.Logger.Error("MySQL配置: 数据库主机、端口或数据库名未配置", zap.Any("mysqlConfig", mysqlConfig))
	}
	// 2: 创建mysql dsn
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=%t&loc=%s", mysqlConfig.User, mysqlConfig.Password, mysqlConfig.Host, mysqlConfig.Port, mysqlConfig.Database, mysqlConfig.Charset, mysqlConfig.ParseTime, mysqlConfig.TimeZone)
	// 3: 设置gorm配置
	gormConfig := setGormConfig()
	// 4: 创建mysql客户端
	client, err := gorm.Open(mysql.Open(dsn), gormConfig)
	if err != nil {
		global.Logger.Error("MySQL配置: 创建客户端连接失败", zap.String("dsn", dsn), zap.Any("err", err))
	}
	// 5: 配置连接池
	sqlDB, err := client.DB()
	if err != nil {
		global.Logger.Error("MySQL配置: 配置链接池失败", zap.Error(err))
	}
	sqlDB.SetMaxIdleConns(mysqlConfig.MaxIdleConns)       // 最大空闲连接
	sqlDB.SetMaxOpenConns(mysqlConfig.MaxOpenConns)       // 最大打开连接
	sqlDB.SetConnMaxLifetime(mysqlConfig.ConnMaxLifetime) // 连接最大生命周期
	global.Logger.Info("MySQL配置: 配置链接池成功", zap.Int("maxIdleConns", mysqlConfig.MaxIdleConns), zap.Int("maxOpenConns", mysqlConfig.MaxOpenConns), zap.Duration("connMaxLifetime", mysqlConfig.ConnMaxLifetime))

	// 自动设置Id
	// 判断Id字段类型是否是自定义类型, 如果是自定义类型且是主键 ,并且为空, 则生成雪花Id, 否则不生成
	if err = client.Callback().Create().Before("gorm:create").Register("id", func(db *gorm.DB) {
		field := db.Statement.Schema.LookUpField("Id")
		if field.FieldType.String() == "global.SnowFlakeId" && field.PrimaryKey && (field.NotNull || field.DefaultValue == "") {
			db.Statement.SetColumn("Id", global.Node.Generate().String())
		}
	}); err != nil {
		global.Logger.Error("MySQL配置: 自动设置雪花ID失败", zap.Any("err", err))
	}

	// 赋值给全局变量
	global.DBClient = client

	// 是否调用数据迁移
	if mysqlConfig.AutoMigrate {
		migrateModels := getMigrationModels()
		if len(migrateModels) == 0 {
			global.Logger.Warn("MySQL配置: 自动迁移已启用，但未配置迁移模型")
		} else {
			if err := client.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(migrateModels...); err != nil {
				global.Logger.Error("MySQL配置: 数据迁移失败", zap.Error(err))
			}
			global.Logger.Info("MySQL配置: 数据迁移成功", zap.Int("models", len(migrateModels)))
		}
	}
	global.Logger.Info("MySQL配置: 创建客户端连接成功", zap.String("dsn", dsn))
	return client
}

// setGormConfig
// Title    				setGormConfig
// Description   			Gorm配置: 慢SQL阈值、日志级别、日志文件路径等信息配置
// Auth      				alittlexincan@163.com   		时间（2024/07/17 11:36）
// Update   				alittlexincan@163.com  		 	时间（2024/07/17 11:36）
// Return    				nil     						无返回值
func setGormConfig() *gorm.Config {

	if mysqlConfig.LogPath == "" {
		mysqlConfig.LogPath = filepath.Join(global.AppConfig.Business.LogDir, global.AppConfig.Service.Name, "mysql", "sql.log")
	}
	// 1: 创建日志文件
	if err := os.MkdirAll(filepath.Dir(mysqlConfig.LogPath), 0755); err != nil {
		global.Logger.Error("Gorm 配置: 创建SQL存储目录失败", zap.String("mysqlConfig.LogPath", mysqlConfig.LogPath), zap.Error(err))
	}
	file, err := os.OpenFile(mysqlConfig.LogPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm)
	if err != nil {
		global.Logger.Error("Gorm 配置: 创建SQL存储目录失败", zap.String("mysqlConfig.LogPath", mysqlConfig.LogPath), zap.Error(err))
		panic(err)
	}
	defer func(file *os.File) {
		err := file.Close()
		if err != nil {
			global.Logger.Error("Gorm 配置: 关闭日志文件失败", zap.String("filePath", mysqlConfig.LogPath), zap.Error(err))
		}
	}(file)
	// 日志级别映射 error、info、warn, debug
	logLevelMap := map[string]logger.LogLevel{
		"error": logger.Error,
		"info":  logger.Info,
		"warn":  logger.Warn,
		"debug": logger.Info, // debug映射为info级别
	}
	// 2: 创建日志, 获取日志级别
	logLevel, ok := logLevelMap[mysqlConfig.LogLevel]
	if !ok {
		logLevel = logger.Error
	}

	// 3: 慢SQL阈值
	slowThreshold, err := time.ParseDuration(mysqlConfig.SlowSql)
	if err != nil {
		slowThreshold = 200 * time.Millisecond // 默认值
	}

	// 4: 同时输出到控制台和文件
	logWriter := io.MultiWriter(os.Stdout, file)
	newLogger := logger.New(
		log.New(logWriter, "\r\n", log.LstdFlags),
		logger.Config{
			SlowThreshold:             slowThreshold,
			LogLevel:                  logLevel,
			IgnoreRecordNotFoundError: mysqlConfig.IgnoreRecordNotFoundError,
			Colorful:                  true,
		})
	// 5: 创建gorm配置
	gormConfig := &gorm.Config{
		SkipDefaultTransaction: global.AppConfig.Gorm.SkipDefaultTx, //是否跳过默认事务
		NamingStrategy: schema.NamingStrategy{
			TablePrefix:   global.AppConfig.Gorm.TablePrefix,
			SingularTable: global.AppConfig.Gorm.SingularTable,
		}, // 命名策略
		PrepareStmt:                              global.AppConfig.Gorm.PreparedStmt,    // 执行任何SQL时都会创建一个prepared statement并将其缓存，以提高后续的效率
		DisableForeignKeyConstraintWhenMigrating: global.AppConfig.Gorm.CloseForeignKey, //在AutoMigrate 或 CreateTable 时，GORM 会自动创建外键约束，若要禁用该特性，可将其设置为 true
	}
	gormConfig.Logger = newLogger
	global.Logger.Info("Gorm 配置: 创建日志存储成功", zap.Any("path", mysqlConfig.LogPath))
	return gormConfig
}

// getMigrationModels
// Title    				getMigrationModels
// Description   			需要迁移的模型数据库表
// Auth      				alittlexincan@163.com   		时间（2024/07/17 11:36）
// Update   				alittlexincan@163.com   		时间（2024/07/17 11:36）
// Return    				interface{}     				数据库表模型
func getMigrationModels() []interface{} {
	// 实际项目中应从配置中获取模型列表
	// 这里返回空切片，实际实现应替换为具体模型
	return []interface{}{}
}
